package cn.coder.toolkit;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

/**
 * FTP工具类
 * <p>
 * 提供单个文件的上传下载功能
 * cd,ls,rm,rmdir等命令可通过ChannelSftp直接执行, 无需开发对应功能
 * 注意:
 * 1.针对所有远程路径的操作都需要对路径以ISO-8859-1编码,这样才能支持中文路径
 * =============================================================================================
 * String filename = new String(remoteFileAbsolutePath.getBytes(), StandardCharsets.ISO_8859_1);
 * =============================================================================================
 *
 * @author com.mrathena on 2019/5/27 11:44
 */
@Slf4j
public final class FtpKit {

	public static void main(String[] args) {
		FTPClient ftp = FtpKit.connect("172.17.45.20", 21, "redbagftp", "redbagftp78@78F");
		FtpKit.upload(ftp, new File("C:\\Users\\com.mrathena\\Desktop\\java开发手册[华山版][1.5.0].pdf"), "/upload");
		FtpKit.download(ftp, "/upload/java开发手册[华山版][1.5.0].pdf", new File("C:\\Users\\com.mrathena\\Desktop\\aaaaaaa.pdf"));
		FtpKit.delete(ftp, "/upload/java开发手册[华山版][1.5.0].pdf");
		FtpKit.close(ftp);
	}

	private FtpKit() {}

	/**
	 * 连接FTP服务器,获取FTP连接通道
	 *
	 * @param host     主机
	 * @param port     端口
	 * @param username 用户名
	 * @param password 密码
	 */
	public static FTPClient connect(String host, int port, String username, String password) {
		try {
			FTPClient client = new FTPClient();
			client.setConnectTimeout(1000);
			client.connect(host, port);
			if (!client.login(username, password)) {
				client.disconnect();
				throw new RuntimeException("Login Failure");
			}
			return client;
		} catch (Throwable cause) {
			throw new RuntimeException(cause);
		}
	}

	/**
	 * 上传文件,会覆盖原文件
	 *
	 * @param client                         ftp
	 * @param localFileAbsolutePath       本地文件绝对路径
	 * @param remoteFileName              远程文件名称
	 * @param remoteDirectoryAbsolutePath 远程目录绝对路径
	 */
	public static void upload(FTPClient client, File localFileAbsolutePath, String remoteFileName, String remoteDirectoryAbsolutePath) {
		try {
			String encodeRemoteDirectoryAbsolutePath = new String(remoteDirectoryAbsolutePath.getBytes(), StandardCharsets.ISO_8859_1);
			if (!client.changeWorkingDirectory(encodeRemoteDirectoryAbsolutePath)) {
				throw new RuntimeException("切换工作路径失败,可能是目标路径不存在");
			}
			client.setBufferSize(1024 * 1024 * 4);
			client.setFileType(FTP.BINARY_FILE_TYPE);
			client.enterLocalPassiveMode();
			try (InputStream inputStream = new FileInputStream(localFileAbsolutePath)) {
				String encodeRemoteFileName = new String(remoteFileName.getBytes(), StandardCharsets.ISO_8859_1);
				if (!client.storeFile(encodeRemoteFileName, inputStream)) {
					throw new RuntimeException("上传文件失败,可能是没有权限");
				}
			}
		} catch (Throwable cause) {
			throw new RuntimeException(cause);
		}
	}

	/**
	 * 上传文件,会覆盖原文件
	 *
	 * @param client                         ftp
	 * @param localFileAbsolutePath       本地文件绝对路径
	 * @param remoteDirectoryAbsolutePath 远程目录绝对路径
	 */
	public static void upload(FTPClient client, File localFileAbsolutePath, String remoteDirectoryAbsolutePath) {
		upload(client, localFileAbsolutePath, localFileAbsolutePath.getName(), remoteDirectoryAbsolutePath);
	}

	/**
	 * 下载文件,会覆盖原文件
	 *
	 * @param client                    ftp
	 * @param remoteFileAbsolutePath 远程文件绝对路径
	 * @param localFileAbsolutePath  本地文件绝对路径
	 */
	private static void download(FTPClient client, String remoteFileAbsolutePath, File localFileAbsolutePath) {
		try {
			boolean localFileExist = localFileAbsolutePath.exists();
			try (OutputStream outputStream = new FileOutputStream(localFileAbsolutePath)) {
				String filename = new String(remoteFileAbsolutePath.getBytes(), StandardCharsets.ISO_8859_1);
				if (!client.retrieveFile(filename, outputStream)) {
					// 如果localFileAbsolutePath原本不存在,则删除该文件
					if (!localFileExist) {
						try {
							outputStream.close();
							if (!localFileAbsolutePath.delete()) {
								log.debug("FTP: delete temp file unsuccessful");
							}
						} catch (Exception e) {
							log.debug("FTP: delete temp file unsuccessful");
						}
					}
					throw new RuntimeException("下载文件失败,可能是要目标文件不存在");
				}
			}
		} catch (Throwable cause) {
			throw new RuntimeException(cause);
		}
	}

	/**
	 * 删除单个文件
	 *
	 * @param client                    ftp
	 * @param remoteFileAbsolutePath 远程文件绝对路径
	 */
	private static void delete(FTPClient client, String remoteFileAbsolutePath) {
		try {
			String filename = new String(remoteFileAbsolutePath.getBytes(), StandardCharsets.ISO_8859_1);
			if (!client.deleteFile(filename)) {
				throw new RuntimeException("删除文件失败,可能是没有权限或目标文件不存在");
			}
		} catch (Throwable cause) {
			throw new RuntimeException(cause);
		}
	}

	/**
	 * 关闭FTP连接
	 *
	 * @param client ftp
	 */
	public static void close(FTPClient client) {
		try {
			if (client.isConnected()) {
				if (!client.logout()) {
					throw new RuntimeException("关闭连接失败,可能是连接在占用中");
				}
				client.disconnect();
			}
		} catch (Throwable cause) {
			throw new RuntimeException(cause);
		}
	}

}
